/*
========================================================================================================================
 Project         Rivelatore di radiazioni RAMAYA (RAdiation Monitor with Arduino Yet Another)
 Hardware        Prototipo sviluppato con Arduino2009, LM35DZ,
                 sonda STS-5 e radiation detector kit by http://www.rhelectronics.net/store/radiation-detector-geiger-counter-diy-kit-second-edition.html 
 Versione        0.beta
 Data inizio     21 gennaio 2014.
 Rel 1.0         nya
 Iniziato da     Enrico Fasciani
 repository      http://code.google.com/p/ramaya

 Funzionalità:
 pulsante sx:
    Stato batteria
    temperatura (opt LM35DZ)
    tempo trascorso
    totale impulsi radioattivi rilevati
 pulsante dx:
    CPM e uSv/h campionati al minuto.
    CPM e uSv/h valore di picco rilevato dall'accensione.
    CPM e uSv/h totale CPM dal momento dell'accensione e uSv/h.

 bug della versione 0.beta
    mancano decimali temperatura
    ricalcolare ratio partitore resistivo batteria
    check conversione CPM -> Sieverts per hour
    TO DO: led d'allarme soglia radioattiva superata.   
========================================================================================================================
*/

#include <LiquidCrystal.h>   
#include "define.h"

boolean noTemp= false;                   // 1: LM35DZ Disconnesso  0: collegato. 
volatile float sensTempC=0;              // lettura analogica della temperatura in Celsius.
volatile float sensVcc=0 ;               // Lettura analogica tesione batteria da partitore resistivo
unsigned long upTime=0;                  // millisecondi trascorsi dall'accensione.
long checkPointMillis=0;                 // millisecondi trascorsi dall'ultima rilevazione temporale base 10000 millis().
unsigned long hh=0;
unsigned long mm=0;
unsigned long ss=0;
int vBatt[3] = {0,0,0};
int gradiTemp[3] = {0,0,0};
unsigned int countSxClick = 0;           // Conta click bottone sinistro
unsigned int countDxClick = 0;           // Conta click bottone destro
unsigned int msgId = 100;                // Identificativo tipo messaggio display
unsigned long protonCount = 0;           // contatore degli impulsi radioattivi;
unsigned long totalCPM = 0;
unsigned long totaluSvHour = 0;
unsigned int CpmCount = 0;
unsigned int cpmCount60000 = 0;
float uSvHour = 0.0000;
unsigned int numNotches=0;
unsigned int maxCpm=0;                   // Valore di picco raggiunto dal CPM.
unsigned int maxuSvHour=0;               // idem per uSv/h.
byte cursLcdFull[8] = {                  // carattere personalizzato per indicare tacca batteria carica e livello radioattività.
  B11111,
  B11111, 
  B11111, 
  B11111, 
  B11111,
  B11111, 
  B11111,
};
byte cursLcdEmpty[8] = {                 // carattere personalizzato per indicare tacca batteria scarica
  B11111,
  B10001, 
  B10001, 
  B10001, 
  B10001,
  B10001, 
  B11111,
};
byte cursLcdCelsius[8] = {                 // carattere personalizzato per indicare il grado Celsius.
  B00110,
  B01001, 
  B01001, 
  B00110, 
  B00000,
  B00000, 
  B00000
};

ISR(INT0_vect)
{                                                                       // Istruzioni da eseguire ogni volta che arriva un IRQ dal sensore Geiger
  protonCount++;
  CpmCount++;
}

void setup() {
  Serial.begin(9600);                    // set default serial port 9600, N, 8, 1
  pinMode(PROBE_IRQ, INPUT);             // input port geiger pulse
  pinMode(BUTTON_SX, INPUT);             // pulsante sinistro
  digitalWrite(BUTTON_SX,HIGH);          // pullup reistor on 
  pinMode(BUTTON_DX, INPUT);             // pulsante destro  
  digitalWrite(BUTTON_DX,HIGH);          // pullup reistor on 
  pinMode(LM35DZ_PIN,INPUT);             // Analog IN #1 Sensore temperatura LM35DZ.
  analogRead(PROBE_VCC);
  pinMode(LED_PIN,OUTPUT);            
  pinMode(LED_RED,OUTPUT);

  EIMSK |= ( 1 << INT0);                 // Enable INT0 interrupt (EIMSK External Interrupt Mask Register) 
  EICRA |= ( 1 << ISC01);                // EICRA External Interrupt Control Register A ISCOx per INT0 su PIN2; 
  EICRA |= ( 0 << ISC00);                // ISC0[1-0] pos.: 00: LOW | 11: RISING | 01: ANY CHANGE | 10: FALLING

  Blink(LED_PIN,3);                      // Blink LED
  Blink(LED_RED,3);                      // Blink LED

  lcd.createChar(1, cursLcdCelsius);     // Override LCD della definizione bitmap di alcuni caratteri ascii  
  lcd.createChar(2, cursLcdFull);  
  lcd.createChar(3, cursLcdEmpty);
  lcd.begin(16,2);                       // Seup LCD 16x2
  updLcdDisplay(msgId);                  // Welcome mgs.
  delay(LCD_DELAY);                      // Ti da il tempo di leggerlo
}

void loop()
{
  calcRadiation();
  checkButton();
  updLcdDisplay(msgId);
  delay(LCD_DELAY);                     
}
//========================================================================================================================
//--Routinres -----------------------------------
//========================================================================================================================

void checkButton()
{
  if (debounce(BUTTON_SX))                              // Check pulsante SX 
  {
    countSxClick++;
    switch (countSxClick)
    {
    case 1:                                             // Welcam message
      msgId=100;    
      break;
    case 2:                                             // Tensione batteria
      msgId=200;
      break;
    case 3:                                             // temperatura.
      msgId=300;                                        
      break;
    case 4:                                             // Uptime display
      msgId=400;    
      break;
    case 5:                                             // Totale impulsi rilevati dall'accensione.
      msgId=500;    
      break;
    default:
      countSxClick=0;       
      return;
    }
  }
  if (debounce(BUTTON_DX))                              // Check pulsante SX 
  {
    countDxClick++;
    switch (countDxClick)
    {
    case 1:  
      msgId=2000;    
      break;
    case 2:  
      msgId=2100;    
      break;
    default:
      countDxClick=0;       
      return;
    }
  }
}

void updLcdDisplay(int msgNum)                                             
{
  lcd.clear();                                          // pulizia schermo
  lcd.setCursor(0,0);                                   // posizione per inizio scrittura
  switch (msgNum)
  {
  case 100:                                             // indica su LCd la versione sw. Vale come Welcome msg.
    lcd.print("Rilevatore BADA");                       // Based on Arduino Digital Analizer.
    lcd.setCursor(0,1);
    lcd.print("ver. ");
    lcd.print(BADA_VER);
    break;
  case 200:                                             // indica su LCD dello stato della batteria
    readVcc();                                          //Acquisizione lettura tensione batteria
    lcd.print("Batteria: "); 
    if (vBatt[0] > 0)
    {
      lcd.print(vBatt[0]);
    } 
    else 
    {
      lcd.print(" ");
    }
    lcd.print(vBatt[1]); 
    lcd.print("."); 
    lcd.print(vBatt[2]);
    lcd.print(" V");  
    barLcd8(numNotches, 0, 1);                                  
    break;
  case 300:                                           // indica su LCd la temperatura ambiente
    readTemp();                                       //  acquisizione valore da sonda LM35DZ
    lcd.print("Temperatura int.");  
    lcd.setCursor(0,1);
    lcd.print( gradiTemp[0] ); 
    lcd.print( gradiTemp[1] );
    lcd.print("."); 
    lcd.print( gradiTemp[2] );
    lcd.print(" C");
    lcd.write(1);
    break;
  case 400:  
    timeRun();    
    lcd.print("Attivo da ");      
    lcd.setCursor(0,1);
    lcd.print(hh);lcd.print(":");lcd.print(mm);lcd.print(":");lcd.print(ss); lcd.print(" (h:m:s)");
    break;
  case 500:                            
    lcd.print("Radioattivita'");      
    lcd.setCursor(0,1);
    lcd.print("impulsi: ");lcd.print("<");lcd.print(protonCount);lcd.print(">");
    break;
  case 2000:     
    lcd.print("CPM:");
    lcd.print(cpmCount60000);
    barLcd8(numNotches, 8, 0);                                  
    lcd.setCursor(0, 1);
    lcd.write(228);
    lcd.print("Sv/h:");
    lcd.print(uSvHour,4);
    break;
  case 2100:
    lcd.print("max CPM:");
    lcd.print(maxCpm);
    lcd.setCursor(0, 1);
    lcd.print("max ");
    lcd.write(228);
    lcd.print("Sv/h:");
    lcd.print(maxuSvHour,4);
    break;
  case 2200: 
    calcExpo();  
    lcd.print("Totale CPM:");
    lcd.print(totalCPM);
    lcd.setCursor(0, 1);
    lcd.print("Totale ");
    lcd.write(228);
    lcd.print("Sv/h:");
    lcd.print(totaluSvHour,4);
    break;
  default: 
    lcd.print("ATTENZIONE!");
    lcd.setCursor(0,1);
    lcd.print("ANOMALIA MESSAG.");
    return;
  }
}

void calcRadiation()
{
  if (millis()-checkPointMillis < 60000)
  {
    return;
  }
  cpmCount60000=CpmCount;
  uSvHour = cpmCount60000 * TUBE_RATIO;
  numNotches=(uSvHour/NOTCH_RESOLUTION)+1;
  if (maxCpm < cpmCount60000)
  {
    maxCpm = cpmCount60000;
    maxuSvHour = uSvHour;
  }
  checkPointMillis = millis();
  CpmCount=0;
}

void calcExpo()
{
  totalCPM=(protonCount) / (millis()/60);
  totaluSvHour=totalCPM * TUBE_RATIO;
}

void readVcc()                                             // Lettura tensione d'alimentazione e valorizzazione esponiRiga1
{
  delay(200);                                              //switch ADC
  sensVcc = analogRead(PROBE_VCC)*0.0147;                  // 0.0049 * rapporto partitore resistivo
  volatile int tempV=sensVcc;   
  vBatt[0] = tempV/10;
  vBatt[1] = tempV%10;
  vBatt[2] = ((sensVcc*10)-(tempV*10));
  numNotches=((tempV-5)/0.125);
}

void readTemp()
{
  if (noTemp)
  {
    return;
  }
  delay(200);                                              //switch ADC
  sensTempC = (((analogRead(LM35DZ_PIN))*500)/1024);               // lettura tensione LM35DZ
  volatile int tempC=sensTempC;    // esclusione decimali.
  gradiTemp[0] = tempC/10;
  gradiTemp[1] = tempC%10;
  gradiTemp[2] = ((sensTempC*10)-(tempC*10));
}

void timeRun()
{
  upTime=millis();
  ss = upTime/1000; 
  mm=ss/60; 
  hh=mm/60;
  ss=ss-(mm*60); 
  mm=mm-(hh*60);
}

boolean debounce(int pin)
{
  boolean state;
  boolean previousState;
  previousState = digitalRead(pin); 
  for(int counter=0; counter < debounceDelay; counter++)
  {
    delay(1); 
    state = digitalRead(pin); 
    if( state != previousState)
    {
      counter = 0; 
      previousState = state;
    }
  }
  if(state == LOW) 
    return true;
  else
    return false;
}

void Blink(byte led, byte times)
{ 
  for (byte k=0; k< times; k++)
  {
    digitalWrite(led,HIGH);
    delay (LED_ON_mSec);
    digitalWrite(led,LOW);
    delay (LED_OFF_mSec);
  }
}

void barLcd8(byte notches, int colLcd, byte rowLcd)
{
    lcd.setCursor(colLcd, rowLcd);
    for (int i=0; i<notches;i++)
    {
      lcd.write(2);
    }
    for (int k=notches; k<8;k++)
    {
      lcd.write(3);
    }
}

//=====================================================================================================================================
//----------YUNKYARD--------------------------------------
//=====================================================================================================================================


